
static inline uint32_t get_region_attributes(void)
{
	uint32_t attr;

	return attr;
}

void mpu_disable(void)
{
    uint32_t val;

    /* Force any outstanding transfers to complete before disabling MPU */
    DMB(sy);

    val = read_sctlr();
    val &= ~SCTLR_M_BIT;
    write_sctlr(val);

    DSB();
    ISB();
}

void mpu_enable(void)
{
    uint32_t val;

    DMB(sy);

    val = read_sctlr();
    val |= SCTLR_M_BIT | SCTLR_C_BIT | SCTLR_I_BIT;
    write_sctlr(val);

    DSB();
    ISB();
}

static inline void set_region_number(uint32_t index)
{
	__asm__ volatile("mcr p15, 0, %0, c6, c2, 1" :: "r" (index) :);
}

static inline uint32_t get_prbar(void)
{
	uint32_t prbar;

	__asm__ volatile("mrc p15, 0, %0, c6, c3, 0" : "=r" (prbar) ::);
	return prbar;
}

static inline void set_prbar(uint32_t prbar)
{
	__asm__ volatile("mcr p15, 0, %0, c6, c3, 0" :: "r" (prbar) :);
}

static inline uint32_t get_prlar(void)
{
	uint32_t prlar;

	__asm__ volatile("mrc p15, 0, %0, c6, c3, 1" : "=r" (prlar) ::);
	return prlar;
}


static inline void set_prlar(uint32_t prlar)
{
	__asm__ volatile("mcr p15, 0, %0, c6, c3, 1" :: "r" (prlar) :);
}

/**
 * This internal function returns the access permissions of an MPU region
 * specified by its region index.
 *
 * Note:
 *   The caller must provide a valid region number.
 */
static inline uint32_t get_region_ap(uint32_t r_index)
{
	set_region_number(r_index);

	return (get_region_attributes() & MPU_PRBAR_AP_Msk) >> MPU_PRBAR_AP_Pos;
}

static inline bool u32_add_overflow(uint32_t a, uint32_t b, uint32_t *result)
{
    uint32_t c = a + b;

    *result = c;

    return c < a;
}


/**
 * This internal function checks if region is enabled or not.
 *
 * Note:
 *   The caller must provide a valid region number.
 */
static inline int is_enabled_region(uint32_t index)
{
	set_region_number(index);

	return (get_prlar() & MPU_PRLAR_ENABLE_Msk) ? 1 : 0;
}

/**
 *  Get the number of supported MPU regions.
 */
static inline uint8_t mpu_get_num_regions(void)
{
	uint32_t type;

	__asm__ volatile("mrc p15, 0, %0, c0, c0, 4" : "=r" (type) ::);

	type = (type & MPU_TYPE_DREGION_Msk) >> MPU_TYPE_DREGION_Pos;

	return (uint8_t)type;
}

static inline uint32_t mpu_region_get_base(uint32_t index)
{
	set_region_number(index);

	return get_prbar() & MPU_PRBAR_ADDR_Msk;
}

static inline uint32_t mpu_region_get_limit(uint32_t index)
{
	set_region_number(index);

	uint32_t limit = get_prlar() & MPU_PRLAR_LIMIT_Msk;

	return limit;
}

static inline uint32_t mpu_region_get_size(uint32_t index)
{
	set_region_number(index);

    uint32_t base = get_prbar() & MPU_PRBAR_ADDR_Msk;

	uint32_t limit = get_prlar() & MPU_PRLAR_LIMIT_Msk;

	return limit - base + 1;
}

/**
 * This internal function checks if the given buffer is in the region.
 *
 * Note:
 *   The caller must provide a valid region number.
 */
static inline int is_in_region(uint32_t r_index, uint32_t start, uint32_t size)
{
	uint32_t r_addr_start;
	uint32_t r_size_lshift;
	uint32_t r_addr_end;
	uint32_t end;

	r_addr_start = mpu_region_get_base(r_index);

	r_addr_end = mpu_region_get_limit(r_index);

	size = size == 0 ? 0 : size - 1;
	if (u32_add_overflow(start, size, &end)) {
		return 0;
	}

	if ((start >= r_addr_start) && (end <= r_addr_end)) {
		return 1;
	}

	return 0;
}

static void region_init(const uint32_t index,
                        const struct arm_mpu_region *region_conf)
{
    set_region_number(index);

    set_prbar((region_conf->base & MPU_PRBAR_ADDR_Msk)   |
               (region_conf->attr.xn & MPU_PRBAR_XN_Msk) |
               (region_conf->attr.ap & MPU_PRBAR_AP_Msk) |
               (region_conf->attr.sh & MPU_PRBAR_SH_Msk));

    set_prlar((region_conf->limit & MPU_PRLAR_LIMIT_Msk) |
                ((region_conf->attr.mair_idx << MPU_PRLAR_AttrIndx_Pos) & MPU_PRLAR_AttrIndx_Msk) |
                MPU_PRLAR_ENABLE_Msk);
}

